import {
  AmbientLight,
  Box3,
  Camera,
  Color,
  DirectionalLight,
  Euler,
  Group,
  Light,
  LoadingManager,
  Material,
  Matrix4,
  Mesh,
  Raycaster,
  MeshBasicMaterial,
  MeshStandardMaterial,
  Object3D,
  OrthographicCamera,
  PMREMGenerator,
  PerspectiveCamera,
  REVISION,
  Scene,
  Texture,
  GridHelper,
  Vector3,
  Vector2,
  WebGLRenderer,
} from "three";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";
import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.js";
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
import { PRESET_COLOR } from "./color";

const MANAGER = new LoadingManager();
const THREE_PATH = `https://unpkg.com/three@0.${REVISION}.x`;
const DRACO_LOADER = new DRACOLoader(MANAGER).setDecoderPath(
  `${THREE_PATH}/examples/jsm/libs/draco/gltf/`
);
const KTX2_LOADER = new KTX2Loader(MANAGER).setTranscoderPath(
  `${THREE_PATH}/examples/jsm/libs/basis/`
);
let scenes = new Scene();
class Viewer {
  el;
  options;
  renderer;
  scene;
  pmremGenerator;
  neutralEnvironment;
  gltf;
  outlinePass;
  renderPass;
  composer;
  content;
  controls;
  materials = [];
  colors = new Map();
  presetCameras = [];
  imgData = [];
  lights = [];
  activeCamera;
  backgroundColor;
  currentMaterial;
  currentColor;
  scaleFactor = 1;
  state = {
    page: 0,
    bgColor: 0xffffff,
    directIntensity: 0.8 * Math.PI,
  };

  constructor(el, options) {
    this.el = el;
    this.options = options;
    this.renderer = window.renderer = new WebGLRenderer({
      canvas: el,
      antialias: true,
      logarithmicDepthBuffer: true,
      precision: "highp",
      preserveDrawingBuffer: true,
    });
    this.renderer.useLegacyLights = false;
    this.renderer.shadowMap.enabled = true;
    this.renderer.setClearColor(0xcccccc);
    this.renderer.setPixelRatio(window.devicePixelRatio * 4);

    this.backgroundColor = new Color(this.state.bgColor);
    scenes = new Scene();
    scenes.background = this.backgroundColor;

    this.pmremGenerator = new PMREMGenerator(this.renderer);
    this.pmremGenerator.compileEquirectangularShader();

    this.neutralEnvironment = this.pmremGenerator.fromScene(
      new RoomEnvironment()
    ).texture;
// 监听画面变化，更新渲染画面
 
 
//根据实际情况设置
// window.addEventListener("resize", () => {
//   // 更新摄像头
//   camera.aspect = dom.offsetWidth / dom.offsetHeight
//   //   更新摄像机的投影矩阵
//   camera.updateProjectionMatrix()
//   //   更新渲染器
//   renderer.setSize(dom.offsetWidth, dom.offsetHeight)
//   //   设置渲染器的像素比
//   renderer.setPixelRatio(dom.devicePixelRatio)
// }) 
    // this.renderer.domElement.addEventListener("click", (event) => {
    //   const { offsetX, offsetY } = event;
    //   let div3D = document.getElementById("myCanvas");
    //   // 通过鼠标点击的位置计算出raycaster所需要的点的位置，以屏幕中心为原点，值的范围为-1到1.
    //   let div3DLeft = div3D.getBoundingClientRect().left;
    //   let div3DTop = div3D.getBoundingClientRect().top;
    //   let x = ((event.clientX - div3DLeft) / div3D.clientWidth) * 2 - 1;
    //   let y = -((event.clientY - div3DTop) / div3D.clientHeight) * 2 + 1;

    //   const mousePoint = new Vector2(x, y);
    //   const raycaster = new Raycaster();
    //   // 设置鼠标位置和参考相机
    //   raycaster.setFromCamera(mousePoint, this.activeCamera);
    //   // 鼠标点击对应的物体（所有鼠标映射到的物体，包括被遮挡的）
    //   const intersects = raycaster.intersectObjects(scenes.children, true);
    //   // 过滤网格和地面
    //   const intersect = intersects.filter(
    //     (intersect) =>
    //       !(intersect.object instanceof GridHelper) &&
    //       intersect.object.name !== "plane"
    //   )[0];
    //   const hexString = intersect.object.material.color.getHexString();

    //   for (let [key, value] of this.colors) {
    //     if (value == "#" + hexString) {
    //       this.currentMaterial = key;
    //     }
    //   }
    //   this.currentColor = "#" + hexString;
    //   const currentMaterials = {
    //     currentMaterial: this.currentMaterial,
    //     currentColor: this.currentColor,
    //   };
    //   window.sessionStorage.setItem(
    //     "currentMaterial",
    //     JSON.stringify(currentMaterials)
    //   );
    //   if (intersect) {
    //     // intersect.object.material.color.set( 0xff0000 );
    //   }
    // });
  }
  getMaterial(event) {
    let div3D = document.getElementById("myCanvas");
    // 通过鼠标点击的位置计算出raycaster所需要的点的位置，以屏幕中心为原点，值的范围为-1到1.
    let div3DLeft = div3D.getBoundingClientRect().left;
    let div3DTop = div3D.getBoundingClientRect().top;
    let x = ((event.clientX - div3DLeft) / div3D.clientWidth) * 2 - 1;
    let y = -((event.clientY - div3DTop) / div3D.clientHeight) * 2 + 1;

    const mousePoint = new Vector2(x, y);
    const raycaster = new Raycaster();
    return new Promise((resolve, reject) => {
      raycaster.setFromCamera(mousePoint, this.activeCamera);
      // 鼠标点击对应的物体（所有鼠标映射到的物体，包括被遮挡的）
      const intersects = raycaster.intersectObjects(scenes.children, true);
      // 过滤网格和地面
      const intersect = intersects.filter(
        (intersect) =>
          !(intersect.object instanceof GridHelper) &&
          intersect.object.name !== "plane"
      )[0];
      const hexString = intersect.object.material.color.getHexString();
      let currentMaterials = "";
      for (let [key, value] of this.colors) {
        if (value == "#" + hexString) {
          currentMaterials = key;
        }
      }

      this.currentColor = "#" + hexString;
      if (currentMaterials != "") {
        resolve([currentMaterials, this.currentColor]);
      }
    });
  }
  init(length) {
    let flag = false;
    if (this.gltf?.cameras?.length) {
      for (let i = 0; i < length * 2; i++) {
        const camera = this.gltf?.cameras[Math.floor(i / 2)];
        this.render(flag, i);
        // this.setCamera(camera);
        flag = !flag;
      }
    }
    return new Promise((resolve, reject) => {
      resolve([this.colors, scenes]);
    });
  }
  render(colorize, i = 0) {
    scenes.traverse((object) => {
      if (object instanceof Mesh) {
        if (colorize) {
          const { uuid, name, transparent, side } = object.material;
          let color = this.colors.get(name);
          if (!color) {
            color = PRESET_COLOR[this.colors.size];
            this.colors.set(name, color);
          }
          const material = color
            ? new MeshBasicMaterial({
                color: color,
              })
            : object.material;
          material.uuid = uuid;
          material.name = name;
          material.transparent = transparent;
          material.side = side;
          object.material = material;
        } else {
          const { uuid } = object.material;
          const material = this.materials.find(
            ({ uuid: originId }) => originId === uuid
          );
          object.material = material;
        }
      }
    });
    if (this.gltf?.cameras?.length) {
      const camera = this.gltf?.cameras?.[Math.floor(i / 2)];
      this.setCamera(camera, colorize);
    } else {
      const camera = this.presetCameras?.[i];
      this.setCamera(camera);
    }

    this.updateEnvironment();
    if (!this.activeCamera) return;
    this.renderer.clear();
    this.renderer.render(scenes, this.activeCamera);
    // if (this.composer) {
    //     this.composer.render()
    //   }
  }

  load(url) {
    this.reset();
    return new Promise((resolve, reject) => {
      const loader = new GLTFLoader(MANAGER)
        .setCrossOrigin("anonymous")
        .setDRACOLoader(DRACO_LOADER)
        .setKTX2Loader(KTX2_LOADER.detectSupport(this.renderer))
        .setMeshoptDecoder(MeshoptDecoder);

      loader.load(
        url,
        (gltf) => {
          const scene = gltf.scene || gltf.scenes[0];

          if (!scene) {
            // Valid, but not supported by this viewer.
            throw new Error(
              "This model contains no scene, and cannot be viewed here. However," +
                " it may contain individual 3D resources."
            );
          }

          this.gltf = gltf;
          this.setPresetCameras(gltf);
          this.setContent(scene);
          // this.render(false);

          resolve([gltf, this.materials, this.colors]);
        },
        undefined,
        reject
      );
    });
  }
  setCurrent(active) {
    console.log("setcurrent", this.imgData[active].colorType, active);
    this.render(this.imgData[active].colorType, active);
    // this.setCamera(this.imgData[active].camera,this.imgData[active].colorType,true)
  }
  fetchBlob(url) {
    return fetch(url)
      .then((response) => {
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        return response.blob();
      })
      .catch((error) => {
        console.error("Fetch error:", error);
      });
  }
  setPresetCameras(gltf) {
    if (gltf?.cameras?.length) return;
    let div3D = document.getElementById("myCanvas");
    const object = gltf.scene;
    const box = new Box3().setFromObject(object);
    const { min, max } = box;
    const center = box.getCenter(new Vector3());
    const h = (max.z - min.z) * 1.02;
    // const width =div3D.clientWidth;
    // const height = div3D.clientHeight;
    const width = window.innerWidth;
    const height = window.innerHeight;
    const scale = h / height;
    for (let i = 0; i < 4; ++i) {
      const camera = new OrthographicCamera(
        (width / -2) * this.scaleFactor * scale,
        (width / 2) * this.scaleFactor * scale,
        (height / 2) * this.scaleFactor * scale,
        (height / -2) * this.scaleFactor * scale
      );
      const pos = new Vector3(
        center.x - Math.sin((Math.PI / 2) * i) * (max.x - min.x),
        center.y - Math.cos((Math.PI / 2) * i) * (max.y - min.y),
        center.z
      );
      const target = new Vector3(center.x, center.y, center.z);
      camera.userData.position = pos;
      camera.userData.lookat = target;
      camera.userData.height = h;
      this.presetCameras.push(camera);
    }
  }

  setContent(object) {
    this.clear();
    object.updateMatrixWorld();
    scenes.add(object);
    this.content = object;
    this.scaleFactor = 0.0254;
    scenes.scale.set(this.scaleFactor, this.scaleFactor, this.scaleFactor);
    this.traverseMaterials(object, (material) => {
      if (this.materials.indexOf(material) < 0) this.materials.push(material);
      const isPng = material?.map?.userData?.mimeType === "image/png";
      material.transparent = material.opacity < 1 || isPng ? true : false;
    });
  }

  setCamera(camera, colorType = false, type) {
    if (!camera) return;


          let div3D = document.getElementById("myCanvas");
    //   // 通过鼠标点击的位置计算出raycaster所需要的点的位置，以屏幕中心为原点，值的范围为-1到1.
    //   let div3DLeft = div3D.getBoundingClientRect().left;
    //   let div3DTop = div3D.getBoundingClientRect().top;
    //   let x = ((event.clientX - div3DLeft) / div3D.clientWidth) * 2 - 1;
    //   let y = -((event.clientY - div3DTop) / div3D.clientHeight) * 2 + 1;
    // const width =div3D.clientWidth;
    // const height = div3D.clientHeight;
    const width = window.innerWidth;
    const height = window.innerHeight;
    const { isPerspectiveCamera, far, near, userData } = camera;
    const { position, lookat, fov, up, height: h, center, is_2d } = userData;
    if (isPerspectiveCamera) {
      this.activeCamera = new PerspectiveCamera(fov, width / height, near, far);
    } else {
      const scale = h / height;
      this.activeCamera = new OrthographicCamera(
        (width / -2) * this.scaleFactor * scale,
        (width / 2) * this.scaleFactor * scale,
        (height / 2) * this.scaleFactor * scale,
        (height / -2) * this.scaleFactor * scale
      );
    }
    this.activeCamera.position.set(
      position.x * this.scaleFactor,
      position.y * this.scaleFactor,
      position.z * this.scaleFactor
    );
    const pos = new Vector3(
      position.x * this.scaleFactor,
      position.y * this.scaleFactor,
      position.z * this.scaleFactor
    );
    const target = new Vector3(
      lookat.x * this.scaleFactor,
      lookat.y * this.scaleFactor,
      lookat.z * this.scaleFactor
    );
    const direction = new Vector3(
      position.x - lookat.x,
      position.y - lookat.y,
      position.z - lookat.z
    );
    if (is_2d) {
      const angle = fov * 0.5 * center.y * (Math.PI / 180); // 将60度转换为弧度
      const z = direction.length() * Math.tan(angle) * this.scaleFactor;
      target.z += z;
    }
    this.activeCamera.lookAt(target);
    const rotationMatrix = new Matrix4().lookAt(pos, target, up);
    const euler = new Euler().setFromRotationMatrix(rotationMatrix);
    this.activeCamera.rotation.set(euler.x, euler.y, euler.z);

    this.lights.forEach((light) => light.parent?.remove(light));
    this.lights.length = 0;
    const ambientLight = new AmbientLight(0xffffff, 1.0);
    ambientLight.name = "ambient_light";
    scenes.add(ambientLight);
    this.lights.push(ambientLight);
    const cameraLight = new DirectionalLight(
      0xffffff,
      this.state.directIntensity
    );
    cameraLight.position.set(
      direction.x * this.scaleFactor,
      direction.y * this.scaleFactor,
      direction.z * this.scaleFactor
    );
    scenes.add(cameraLight);
    this.lights.push(cameraLight);

    this.renderer.clear();
    this.renderer.render(scenes, this.activeCamera);
    if (type) return;
    const dataURL = window.renderer.domElement.toDataURL("image/jpg");
    this.imgData.push({
      name: camera.name || "",
      colorType: colorType,
      url: dataURL,
      camera,
    });
  }

  updateEnvironment() {
    // scenes.environment = this.neutralEnvironment;
  }

  traverseMaterials(object, callback) {
    object.traverse((node) => {
      const mesh = node;
      if (!mesh.isMesh) return;
      const materials = Array.isArray(mesh.material)
        ? mesh.material
        : [mesh.material];
      materials.forEach(callback);
    });
  }

  reset() {
    this.state.page = 0;
    this.materials.length = 0;
    this.presetCameras.length = 0;
    this.colors.clear();
  }

  clear() {
    if (!this.content) return;
    scenes.remove(this.content);
    this.content.traverse((node) => {
      const mesh = node;
      if (!mesh.isMesh) return;

      mesh.geometry.dispose();
    });
    this.traverseMaterials(this.content, (material) => {
      for (const key in material) {
        const texture = material[key];
        // const texture = material[key as keyof Material] as Texture;
        if (key !== "envMap" && texture && texture.isTexture) {
          texture.dispose();
        }
      }
    });
  }
}
export default Viewer;
